﻿using System;
using System.Collections.Generic;
using Microsoft.Ccr.Core;

namespace Bettzueche_CCR.Benchmarks {
    /// <summary>
    /// Vgl. generischen und runtime PortSet.
    /// </summary>
    class PortSetTest : CallBackTester {

        Dispatcher dispatcher; //Threadpool
        DispatcherQueue taskQueue; //Queue zum Threadpool
        PortSet<int, double> genericPS; //TestObjekt 1
        PortSet runtimePS;              //TestObjekt 2
        Port<bool> teardownPort;        //interner Port für shutdown-Ops

        /// <summary>
        /// Stellt eine neue Testklasse für PortSet bereit
        /// mit der angegebenen Anzahl Posts je Port.
        /// </summary>
        /// <param name="numOfPosts">Anzahl Wiederholungen = Anzahl Posts ins PortSet</param>
        public PortSetTest(int numOfPosts) {
            this.Repititions = numOfPosts;
            dispatcher = new Dispatcher(0, "ThreadPool");
            taskQueue = new DispatcherQueue("DQueue", dispatcher);
            computing = false;
        }


        /// <summary>
        /// Handler fuer runtime PortSet.
        /// Per Choice jeweils Repitions mal auslesen.
        /// An teardownPort posten, wenn fertig
        /// </summary>
        /// <param name="ps">PortSet</param>
        IEnumerator<ITask> PortSetHandler(PortSet ps) {
            int cntInt = 0;
            int cntDbl = 0;
            int end = 0; //wird incr. wenn i oder d = Repitions

            while (end != 2) {
                yield return Arbiter.Choice(ps);
                Nullable<int> i = ps.Test<int?>(); //Achtung bei Wertetypen! -> NullReferenceException
                if (i != null) {
                    cntInt++;
                    if (cntInt == Repititions) {
                        //teardownPort.Post(true);
                        end++;
                    }
                }
                else {
                    double? d = ps.Test<double>();
                    if (!d.HasValue) {
                        Console.WriteLine("Error at PortSetHandler: retrieved either int nor double");
                        yield break;
                    }
                    cntDbl++;
                    if (cntDbl == Repititions) {
                        //teardownPort.Post(true);
                        end++;
                    }
                }
            }
            teardownPort.Post(true);
            yield break;
        }


        /// <summary>
        /// Handler for generic PortSet.
        /// </summary>
        /// <param name="ps">PortSet to retrieve from</param>
        IEnumerator<ITask> genericHandler(PortSet<int, double> ps) {
            int cntInt = 0;
            int cntDbl = 0;
            int end = 0; //wird incr. wenn i oder d = Repitions
            while (end != 2) {
                yield return Arbiter.Choice(
                    ps,
                    num =>
                    {
                        cntInt++;
                        if (cntInt == Repititions) {
                            end++;
                            //teardownPort.Post(true);
                        }
                    },
                    dbl =>
                    {
                        cntDbl++;
                        if (cntDbl == Repititions) {
                            end++;
                            //teardownPort.Post(true);
                        }
                    }
                );
            }
            teardownPort.Post(true);
            yield break;
        }

        /// <summary>
        /// Beginnt den Test. Erst generischer, dann runtime PortSet of (int,double). Bei beiden Tests
        /// werden Repitions mal in alle Ports des PortSets gepostet. Danach startet die Uhr, wie schnell
        /// die Posts jeweils abgearbeitet werden können.
        /// Ergebnis wird im LogFile festgehalten.
        /// </summary>
        internal void Start() {
            genericPS = new PortSet<int, double>();
            runtimePS = new PortSet(typeof(int?), typeof(double)); //Nullable<int>, weil Test<int>() bei NULL Exc auslöst
            teardownPort = new Port<bool>();

            #region generic PortSet
            //----------- generic PortSet --------------------------------
            // Ports voll hauen
            for (int i = 1; i <= this.Repititions; i++) {
                genericPS.Post(i);
                genericPS.Post(0.0 + i);
            }
            Console.WriteLine("    consuming generic PortSet...");
            computing = true;
            this._startTime = DateTime.Now;
            //
            Arbiter.Activate(
                taskQueue,
                new IterativeTask<PortSet<int, double>>( genericPS, genericHandler  ),
                teardownPort.Receive( bVal => computing = false ) //oneTime Receive
            );

            doSomeStuff(); //busy, until computing=false
            _endTime = DateTime.Now;
            Utilities.WriteLog(LogFile, Repititions, _startTime, (_endTime - _startTime).TotalMilliseconds, "generic PortSet");
            //----------- runteime PortSet --------------------------------------------------
            #endregion

            #region Runtime PortSet
            for (int i = 1; i <= Repititions; i++) {
                runtimePS.PostUnknownType(i);
                runtimePS.PostUnknownType((double)i);
            }
            Console.WriteLine("    consuming runtime PortSet...");
            computing = true;
            this._startTime = DateTime.Now;
            //
            Arbiter.Activate(
                taskQueue,
                new IterativeTask<PortSet>( runtimePS, PortSetHandler ),
                teardownPort.Receive( bVal => computing = false )
            );
            doSomeStuff();
            _endTime = DateTime.Now;
            Utilities.WriteLog(LogFile, Repititions, _startTime, (_endTime - _startTime).TotalMilliseconds, "runtime PortSet");
            //----------- runteime PortSet --------------------------------------------------

            #endregion

            Console.WriteLine("--- End of PortSetTest.Start() ---");
            dispatcher.Dispose();
        }

    }
}
